using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using AreaBuilder.Input;

namespace AreaBuilder.Forms {
    public partial class frmRoom : Form {
        private readonly AreaHolder ah;
        private readonly int existingVNum = -1;
        private ArrayList exitsOrig = new ArrayList();
        private ArrayList extras = new ArrayList();
        private ArrayList extrasOrig = new ArrayList();
        private bool ignoreRandom;

        public frmRoom() {
            InitializeComponent();
        }

        public frmRoom(AreaHolder ah, int value) {
            InitializeComponent();
// ReSharper disable DoNotCallOverridableMethodsInConstructor
            changeColor(tabExits, BackColor);
// ReSharper restore DoNotCallOverridableMethodsInConstructor

            this.ah = ah;
            existingVNum = value;
        }

        private void frmRoom_Load(object sender, EventArgs e) {
            Funcs.fillListType(lstFlags, "/types/room/flag/@type", "/types/room/flag", "");
            Funcs.fillComboType(cmbSectors, "/types/sector/flag/@type", "/types/sector/flag", "");
            Funcs.fillComboType(cmbDoorStateNorth, "/types/door/flag/@type", "/types/door/flag", "");
            Funcs.fillComboType(cmbDoorStateWest, "/types/door/flag/@type", "/types/door/flag", "");
            Funcs.fillComboType(cmbDoorStateEast, "/types/door/flag/@type", "/types/door/flag", "");
            Funcs.fillComboType(cmbDoorStateSouth, "/types/door/flag/@type", "/types/door/flag", "");
            Funcs.fillComboType(cmbDoorStateDown, "/types/door/flag/@type", "/types/door/flag", "");
            Funcs.fillComboType(cmbDoorStateUp, "/types/door/flag/@type", "/types/door/flag", "");

            #region FILL_EVERYTHING

            cmbTargetRoomNorth.Items.Add("");
            cmbTargetRoomWest.Items.Add("");
            cmbTargetRoomEast.Items.Add("");
            cmbTargetRoomSouth.Items.Add("");
            cmbTargetRoomDown.Items.Add("");
            cmbTargetRoomUp.Items.Add("");

            for (int i = 0; i < ah.area.Rooms.Count; i++) {
                var r = (Room)ah.area.Rooms[i];

                cmbTargetRoomNorth.Items.Add(String.Format("#{0} - {1}", r.VNum, r.Title));
                cmbTargetRoomWest.Items.Add(String.Format("#{0} - {1}", r.VNum, r.Title));
                cmbTargetRoomEast.Items.Add(String.Format("#{0} - {1}", r.VNum, r.Title));
                cmbTargetRoomSouth.Items.Add(String.Format("#{0} - {1}", r.VNum, r.Title));
                cmbTargetRoomDown.Items.Add(String.Format("#{0} - {1}", r.VNum, r.Title));
                cmbTargetRoomUp.Items.Add(String.Format("#{0} - {1}", r.VNum, r.Title));
            }

            for (int i = 0; i < ah.area.Items.Count; i++) {
                var it = (Item)ah.area.Items[i];

                if (String.Compare(it.ItemType, "key", true) == 0) {
                    cmbKeyVNumNorth.Items.Add(String.Format("#{0} - {1}", it.VNum, it.Name));
                    cmbKeyVNumWest.Items.Add(String.Format("#{0} - {1}", it.VNum, it.Name));
                    cmbKeyVNumEast.Items.Add(String.Format("#{0} - {1}", it.VNum, it.Name));
                    cmbKeyVNumSouth.Items.Add(String.Format("#{0} - {1}", it.VNum, it.Name));
                    cmbKeyVNumDown.Items.Add(String.Format("#{0} - {1}", it.VNum, it.Name));
                    cmbKeyVNumUp.Items.Add(String.Format("#{0} - {1}", it.VNum, it.Name));
                }
            }

            cmbSectors.SelectedIndex = 0;

            cmbDoorStateNorth.SelectedIndex = 0;
            cmbDoorStateWest.SelectedIndex = 0;
            cmbDoorStateEast.SelectedIndex = 0;
            cmbDoorStateSouth.SelectedIndex = 0;
            cmbDoorStateDown.SelectedIndex = 0;
            cmbDoorStateUp.SelectedIndex = 0;

            #endregion

            if (existingVNum >= 0) {
                Room r = (from Room room in ah.area.Rooms
                          where room.VNum == existingVNum
                          select room).Single();

                txtVNum.Text = r.VNum.ToString();
                txtTitle.Text = r.Title;
                txtDescription.Text = r.Description.Replace("\n", Environment.NewLine);

                trkMana.Value = r.ManaRegen;
                trkHP.Value = r.HPRegen;

                selectRoomFlags(r.RoomFlags);
                cmbSectors.SelectedIndex = Int32.Parse(r.SectorFlags);

                if (r.RoomFlags.IndexOf('G') >= 0) {
                    lblTeleport.Enabled = true;
                    rdoRandom.Enabled = true;
                    rdoTeleport.Enabled = true;

                    rdoTeleport.Text = String.Format("Room #{0}", r.XXValue);

                    if (r.XXValue == 0)
                        rdoRandom.Checked = true;
                    else {
                        ignoreRandom = true;
                        rdoTeleport.Checked = true;
                    }
                }

                extras = r.Extras;
                extrasOrig = r.Extras;

                exitsOrig = r.Exits;

                #region ROOM_EXITS

                for (int i = 0; i < r.Exits.Count; i++) {
                    var re = (RoomExit)r.Exits[i];

                    TextBox description = null;
                    TextBox keyphrase = null;
                    ComboBox state = null;
                    ComboBox keyVNum = null;
                    ComboBox targetRoom = null;
                    CheckBox noKey = null;
                    TabPage page = null;

                    #region SET_EXIT_INFO

                    if (re.Direction == 0) {
                        // North
                        description = txtExitDescNorth;
                        keyphrase = txtDoorKeyphraseNorth;
                        state = cmbDoorStateNorth;
                        keyVNum = cmbKeyVNumNorth;
                        targetRoom = cmbTargetRoomNorth;
                        noKey = chkNoKeyNorth;
                        page = tabNorth;
                    } else if (re.Direction == 1) {
                        // East
                        description = txtExitDescEast;
                        keyphrase = txtDoorKeyphraseEast;
                        state = cmbDoorStateEast;
                        keyVNum = cmbKeyVNumEast;
                        targetRoom = cmbTargetRoomEast;
                        noKey = chkNoKeyEast;
                        page = tabEast;
                    } else if (re.Direction == 2) {
                        // South
                        description = txtExitDescSouth;
                        keyphrase = txtDoorKeyphraseSouth;
                        state = cmbDoorStateSouth;
                        keyVNum = cmbKeyVNumSouth;
                        targetRoom = cmbTargetRoomSouth;
                        noKey = chkNoKeySouth;
                        page = tabSouth;
                    } else if (re.Direction == 3) {
                        // West
                        description = txtExitDescWest;
                        keyphrase = txtDoorKeyphraseWest;
                        state = cmbDoorStateWest;
                        keyVNum = cmbKeyVNumWest;
                        targetRoom = cmbTargetRoomWest;
                        noKey = chkNoKeyWest;
                        page = tabWest;
                    } else if (re.Direction == 4) {
                        // Up
                        description = txtExitDescUp;
                        keyphrase = txtDoorKeyphraseUp;
                        state = cmbDoorStateUp;
                        keyVNum = cmbKeyVNumUp;
                        targetRoom = cmbTargetRoomUp;
                        noKey = chkNoKeyUp;
                        page = tabUp;
                    } else if (re.Direction == 5) {
                        // Down
                        description = txtExitDescDown;
                        keyphrase = txtDoorKeyphraseDown;
                        state = cmbDoorStateDown;
                        keyVNum = cmbKeyVNumDown;
                        targetRoom = cmbTargetRoomDown;
                        noKey = chkNoKeyDown;
                        page = tabDown;
                    }

                    #endregion

                    if (description != null)
                        description.Text = re.Description;
                    
                    if (keyphrase != null)
                        keyphrase.Text = re.Keyphrase;

                    if (state != null)
                        state.SelectedIndex = re.DoorState;

                    if (noKey != null)
                        noKey.Checked = (re.KeyVNum <= 0);

                    // BUG: Possible lack of door key vnum check?
                    // Will check... later.

                    selectRoom(targetRoom, re.LinkVNum);

                    if (page != null && page.Text.IndexOf("* ") < 0)
                        page.Text = String.Format("* {0}", page.Text);
                }

                #endregion

                fillExtraKeys();
            } else {
                if (getNextVnum() < 0) {
                    MessageBox.Show("VNum Range was incremented by 100.", "Hrm...", MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);

                    ah.area.MaxVnum += 100;
                }

                txtVNum.Text = getNextVnum().ToString();
            }
        }

        private void trkMana_ValueChanged(object sender, EventArgs e) {
            lblManaVal.Text = String.Format("{0}%", trkMana.Value);
        }

        private void trkHP_ValueChanged(object sender, EventArgs e) {
            lblHPVal.Text = String.Format("{0}%", trkHP.Value);
        }

        private void lstFlags_SelectedValueChanged(object sender, EventArgs e) {
            if (isTeleportChecked()) {
                lblTeleport.Enabled = true;
                rdoRandom.Enabled = true;
                rdoTeleport.Enabled = true;
            } else {
                lblTeleport.Enabled = false;
                rdoRandom.Enabled = false;
                rdoTeleport.Enabled = false;
            }
        }

        private static int getTeleportIndex() {
            String[] desc = Funcs.getXPath("/types/room/flag").Split('|');

            for (int i = 0; i < desc.Length; i++)
                if (desc[i].ToLower().IndexOf("teleport") >= 0)
                    return i;

            return -1;
        }

        private bool isTeleportChecked() {
            ListBox.SelectedIndexCollection indices = lstFlags.SelectedIndices;
            int index = getTeleportIndex();

            for (int i = 0; i < indices.Count; i++)
                if (indices[i] == index)
                    return true;

            return false;
        }

        private void rdoTeleport_CheckedChanged(object sender, EventArgs e) {
            if (rdoTeleport.Checked && !ignoreRandom) {
                var rooms = new String[ah.area.Rooms.Count];

                IOrderedEnumerable<Room> query = from Room r in ah.area.Rooms
                                                 orderby r.VNum
                                                 select r;

                int i = 0;

                foreach (Room r in query)
                    rooms[i++] = String.Format("#{0} - {1}", r.VNum, r.Title);

                var ib = new InputBox("Please select a room", "Rooms", rooms);
                ib.ShowDialog();

                if (ib.OK) {
                    rdoTeleport.Text = String.Format("Room #{0}", getRoomVnum(ib.selected));
                } else {
                    rdoRandom.Checked = true;
                }
            }

            ignoreRandom = false;
        }

        private static int getRoomVnum(String room) {
            var re = new Regex(@"#(?<number>\d+) ");
            Match m = re.Match(room);

            if (m.Success)
                return Int32.Parse(m.Groups["number"].Value);

            return -1;
        }

        private void chkNoKeyNorth_CheckedChanged(object sender, EventArgs e) {
            cmbKeyVNumNorth.Enabled = !chkNoKeyNorth.Checked;
        }

        private void chkNoKeyWest_CheckedChanged(object sender, EventArgs e) {
            cmbKeyVNumWest.Enabled = !chkNoKeyWest.Checked;
        }

        private void chkNoKeyEast_CheckedChanged(object sender, EventArgs e) {
            cmbKeyVNumEast.Enabled = !chkNoKeyEast.Checked;
        }

        private void chkNoKeySouth_CheckedChanged(object sender, EventArgs e) {
            cmbKeyVNumSouth.Enabled = !chkNoKeySouth.Checked;
        }

        private void chkNoKeyDown_CheckedChanged(object sender, EventArgs e) {
            cmbKeyVNumDown.Enabled = !chkNoKeyDown.Checked;
        }

        private void chkNoKeyUp_CheckedChanged(object sender, EventArgs e) {
            cmbKeyVNumUp.Enabled = !chkNoKeyUp.Checked;
        }

        private static void changeColor(TabControl control, Color color) {
            for (int i = 0; i < control.TabCount; i++)
                control.TabPages[i].BackColor = color;
        }

        private void selectRoomFlags(String flag) {
            String[] flags = Regex.Split(flag, "");

            for (int i = 0; i < flags.Length; i++) {
                if (String.IsNullOrEmpty(flags[i]))
                    continue;

                selectFlag(flags[i]);
            }
        }

        private void selectFlag(String flag) {
            var re = new Regex(@"(?<flag>\w) \-");

            for (int i = 0; i < lstFlags.Items.Count; i++) {
                Match m = re.Match(lstFlags.Items[i].ToString());

                if (m.Success && m.Groups["flag"].Value.Equals(flag)) {
                    lstFlags.SetSelected(i, true);

                    return;
                }
            }
        }

        private static void selectRoom(ComboBox room, int vnum) {
            var re = new Regex(@"#(?<number>\d+) ");

            for (int i = 0; i < room.Items.Count; i++) {
                Match m = re.Match(room.Items[i].ToString());

                if (m.Success && Int32.Parse(m.Groups["number"].Value) == vnum) {
                    room.SelectedIndex = i;

                    return;
                }
            }
        }

        private int getNextVnum() {
            int start = ah.area.MinVnum;
            int end = ah.area.MaxVnum;

            for (int i = start; i < end + 1; i++) {
                if (!validRoomVnum(i))
                    return i;
            }

            return -1;
        }

        private bool validRoomVnum(int vnum) {
            try {
#pragma warning disable 168
                Room r = (from Room room in ah.area.Rooms
#pragma warning restore 168
                          where room.VNum == vnum
                          select room).Single();

                return true;
            } catch (InvalidOperationException) { }

            return false;
        }

        private void btnCancel_Click(object sender, EventArgs e) {
            if (existingVNum >= 0) {
                Room r = (from Room room in ah.area.Rooms
                          where room.VNum == existingVNum
                          select room).Single();

                r.Extras = extrasOrig;
                r.Exits = exitsOrig;
            }

            Dispose();
        }

        private void btnOK_Click(object sender, EventArgs e) {
            Room r = existingVNum >= 0 ? (from Room room in ah.area.Rooms
                                          where room.VNum == existingVNum
                                          select room).Single() : new Room();

            r.VNum = Int32.Parse(txtVNum.Text);
            r.Title = txtTitle.Text;
            r.Description = txtDescription.Text;

            r.RoomFlags = Funcs.getFlags(lstFlags);
            r.SectorFlags = Funcs.getComboFlag(cmbSectors);

            if (isTeleportChecked()) {
                if (rdoTeleport.Checked) {
                    var re = new Regex(@"Room #(?<number>\d+)");
                    Match m = re.Match(rdoTeleport.Text);
                    r.XXValue = Int32.Parse(m.Groups["number"].Value);
                } else
                    r.XXValue = 0;
            } else {
                int temp = ah.area.MinVnum / 100;
                r.XXValue = temp;
            }

            r.ManaRegen = trkMana.Value;
            r.HPRegen = trkHP.Value;

            #region ROOM_EXITS

            ComboBox[] targetRoom = {
                cmbTargetRoomNorth, cmbTargetRoomEast, cmbTargetRoomSouth, cmbTargetRoomWest,
                cmbTargetRoomUp, cmbTargetRoomDown
            };
            TextBox[] exitDesc = {
                txtExitDescNorth, txtExitDescEast, txtExitDescSouth, txtExitDescWest, txtExitDescUp,
                txtExitDescDown
            };
            TextBox[] keyphrase = {
                txtDoorKeyphraseNorth, txtDoorKeyphraseEast, txtDoorKeyphraseSouth,
                txtDoorKeyphraseWest, txtDoorKeyphraseUp, txtDoorKeyphraseDown
            };
            ComboBox[] doorstate = {
                cmbDoorStateNorth, cmbDoorStateEast, cmbDoorStateSouth, cmbDoorStateWest,
                cmbDoorStateUp, cmbDoorStateDown
            };
            CheckBox[] noKey = { chkNoKeyNorth, chkNoKeyEast, chkNoKeySouth, chkNoKeyWest, chkNoKeyUp, chkNoKeyDown };
            ComboBox[] keyVNum = {
                cmbKeyVNumNorth, cmbKeyVNumEast, cmbKeyVNumSouth, cmbKeyVNumWest, cmbKeyVNumUp,
                cmbKeyVNumDown
            };

            r.Exits.Clear();
            for (int i = 0; i < targetRoom.Length; i++) {
                if (targetRoom[i].SelectedIndex > 0) {
                    var re = new RoomExit {
                        Direction = i,
                        Description = exitDesc[i].Text,
                        Keyphrase = keyphrase[i].Text,
                        DoorState = doorstate[i].SelectedIndex
                    };

                    if (noKey[i].Checked || keyVNum[i].SelectedItem == null)
                        re.KeyVNum = 0;
                    else
                        re.KeyVNum = getKeyVNum(keyVNum[i].SelectedItem.ToString());

                    re.LinkVNum = getRoomVnum(targetRoom[i].SelectedItem.ToString());

                    r.Exits.Add(re);
                }
            }

            #endregion

            r.Extras = extras;

            if (existingVNum < 0)
                ah.area.Rooms.Add(r);

            Dispose();
        }

        private void lstExtras_SelectedIndexChanged(object sender, EventArgs e) {
            if (lstExtras.SelectedItem == null)
                return;

            int id = getExtraFromKey(lstExtras.SelectedItem.ToString());

            setExtraInfo(id);
        }

        private void fillExtraKeys() {
            lstExtras.Items.Clear();

            for (int i = 0; i < extras.Count; i++) {
                var re = (RoomExtra)extras[i];

                lstExtras.Items.Add(String.Format("{0}", re.Keyword));
            }
        }

        private int getExtraFromKey(String keyword) {
            for (int i = 0; i < extras.Count; i++) {
                var re = (RoomExtra)extras[i];

                if (re.Keyword.Equals(keyword))
                    return i;
            }

            return -1;
        }

        private void setExtraInfo(int id) {
            var re = (RoomExtra)extras[id];

            txtExtraKey.Text = re.Keyword;
            txtExtraDesc.Text = re.Description.Replace("\n", Environment.NewLine);
        }

        private void btnDelete_Click(object sender, EventArgs e) {
            if (lstExtras.SelectedItem == null)
                return;

            int id = getExtraFromKey(lstExtras.SelectedItem.ToString());

            extras.RemoveAt(id);
            txtExtraDesc.Text = "";
            txtExtraKey.Text = "";

            fillExtraKeys();
        }

        private void btnModify_Click(object sender, EventArgs e) {
            if (lstExtras.SelectedItem == null)
                return;

            int id = getExtraFromKey(lstExtras.SelectedItem.ToString());

            var re = (RoomExtra)extras[id];

            re.Keyword = txtExtraKey.Text;
            re.Description = txtExtraDesc.Text;

            txtExtraDesc.Text = "";
            txtExtraKey.Text = "";

            fillExtraKeys();
        }

        private void btnAdd_Click(object sender, EventArgs e) {
            if (String.IsNullOrEmpty(txtExtraDesc.Text) || String.IsNullOrEmpty(txtExtraKey.Text)) {
                MessageBox.Show("Both fields need to be set!", "Whoops!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            var re = new RoomExtra { Description = txtExtraDesc.Text, Keyword = txtExtraKey.Text };

            extras.Add(re);

            txtExtraDesc.Text = "";
            txtExtraKey.Text = "";

            fillExtraKeys();
        }

        private static int getKeyVNum(String key) {
            var re = new Regex(@"#(?<vnum>\d+) ");
            Match m = re.Match(key);

            if (m.Success)
                return Int32.Parse(m.Groups["vnum"].Value);

            return 0;
        }

        private void btnPrev_Click(object sender, EventArgs e) {
            try {
                int currVnum = Int32.Parse(txtVNum.Text);
                IEnumerable<int> query = from Room r in ah.area.Rooms
                                         where r.VNum < currVnum
                                         orderby r.VNum descending
                                         select r.VNum;
                int newArray = query.First();

                Form newForm = new frmRoom(ah, newArray) { MdiParent = MdiParent };

                newForm.Location = new Point((MdiParent.Size.Width - newForm.Size.Width) / 2 + 40,
                                             (MdiParent.Size.Height - newForm.Size.Height) / 2 - 60);
                newForm.Show();

                btnOK_Click(null, null);
            } catch (InvalidOperationException) {
                MessageBox.Show("There are no more mobs to show!", "Whoops!", MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
            }
        }

        private void btnNext_Click(object sender, EventArgs e) {
            try {
                int currVnum = Int32.Parse(txtVNum.Text);
                IEnumerable<int> query = from Room r in ah.area.Rooms
                                         where r.VNum > currVnum
                                         orderby r.VNum ascending
                                         select r.VNum;
                int newArray = query.First();

                Form newForm = new frmRoom(ah, newArray) { MdiParent = MdiParent };

                newForm.Location = new Point((MdiParent.Size.Width - newForm.Size.Width) / 2 + 40,
                                             (MdiParent.Size.Height - newForm.Size.Height) / 2 - 60);
                newForm.Show();

                btnOK_Click(null, null);
            } catch (InvalidOperationException) {
                MessageBox.Show("There are no more mobs to show!", "Whoops!", MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
            }
        }

        private void btnReverse_Click(object sender, EventArgs e) {
            ComboBox[] targetRoom = {
                cmbTargetRoomNorth, cmbTargetRoomEast, cmbTargetRoomSouth, cmbTargetRoomWest,
                cmbTargetRoomUp, cmbTargetRoomDown
            };
            TextBox[] exitDesc = {
                txtExitDescNorth, txtExitDescEast, txtExitDescSouth, txtExitDescWest, txtExitDescUp,
                txtExitDescDown
            };
            TextBox[] keyphrase = {
                txtDoorKeyphraseNorth, txtDoorKeyphraseEast, txtDoorKeyphraseSouth,
                txtDoorKeyphraseWest, txtDoorKeyphraseUp, txtDoorKeyphraseDown
            };
            ComboBox[] doorstate = {
                cmbDoorStateNorth, cmbDoorStateEast, cmbDoorStateSouth, cmbDoorStateWest,
                cmbDoorStateUp, cmbDoorStateDown
            };
            CheckBox[] noKey = { chkNoKeyNorth, chkNoKeyEast, chkNoKeySouth, chkNoKeyWest, chkNoKeyUp, chkNoKeyDown };
            ComboBox[] keyVNum = {
                cmbKeyVNumNorth, cmbKeyVNumEast, cmbKeyVNumSouth, cmbKeyVNumWest, cmbKeyVNumUp,
                cmbKeyVNumDown
            };

            //                   S  W  N  E  D  U
            int[] directionsI = { 2, 3, 0, 1, 5, 4 };

            if (targetRoom[tabExits.SelectedIndex].SelectedIndex > 0) {
                var re = new RoomExit();
                Room r = (from Room room in ah.area.Rooms
                          where room.VNum == getRoomVnum(targetRoom[tabExits.SelectedIndex].SelectedItem.ToString())
                          select room).Single();

                try {
                    RoomExit rEx = (from RoomExit rExit in r.Exits
                                    where rExit.Direction == directionsI[tabExits.SelectedIndex]
                                    select rExit).Single();

                    r.Exits.Remove(rEx);
                } catch (InvalidOperationException) { }

                re.Direction = directionsI[tabExits.SelectedIndex];
                re.Description = exitDesc[tabExits.SelectedIndex].Text;
                re.Keyphrase = keyphrase[tabExits.SelectedIndex].Text;
                re.DoorState = doorstate[tabExits.SelectedIndex].SelectedIndex;

                if (noKey[tabExits.SelectedIndex].Checked || keyVNum[tabExits.SelectedIndex].SelectedItem == null)
                    re.KeyVNum = 0;
                else
                    re.KeyVNum = getKeyVNum(keyVNum[tabExits.SelectedIndex].SelectedItem.ToString());

                re.LinkVNum = Int32.Parse(txtVNum.Text);

                r.Exits.Add(re);
            }

            MessageBox.Show("Current door has been reversed!", "Yay!", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}